環境依存の設定を解決する Cloud DIのスマートな実現方法
もう1年以上前になりますが、Cloud DIをApache HTTP Serverを例にして紹介するブログを書きました。
その中では、環境変数を利用して利用する設定ファイルを読み替えていました。その読み替えを大元のhttpd.conf
の中で実施しています。
ですが、OS上で動作するミドルウェア/アプリケーションは当然Apache HTTP Server以外にもたくさん存在します。環境依存の設定は至る所に存在することが多いので、そういったもの一つ一つに設定を実施するのは運用時に思わぬ落とし穴にハマることになりそうです。環境依存の設定を実施する場所を一箇所にまとめたいという要望を実現するため、以下の形式で実装してみることにしました。
方針
- 対象OSはAmazon LinuxおよびRHEL系ディストリビューションのLinuxとする。
- 自分自身の環境が開発/ステージング/本番のどれにあたるのか、EC2のEnvironmentタグで区別するものとする。
- 例えば Environent: prd などが設定されているものとする
- 環境依存の設定を切り替えるためのスクリプトを
/etc/environment.d/
以下に配置する - Environmentタグから取得された内容は
/etc/environment.d/environment
にテキストファイルとして配置する - OS起動時のサービスとして
/etc/environment.d/environment
に書き込み、環境依存設定切替のスクリプトを実行する
実装
前提
以下の構成を実行するためには、あらかじめ以下の設定が必要になります。
- EC2の
DescribeTags
APIが利用可能なIAM RoleをEC2インスタンスに紐付けていること - AWS CLIおよびjqがインストールされていること
起動スクリプト
OSの起動時にサービスとして起動するため、/etc/init.d/environment
を以下のような形で配置します。
#!/bin/bash # # /etc/rc.d/init.d/environment # # chkconfig: 345 15 20 # description: setting for environment # . /etc/rc.d/init.d/functions log() { echo $1 | logger } getInstanceTagWithRetry() { attempts=0 maxRetry=5 while :; do aws ec2 describe-tags --region ${region} --filters \ "Name=resource-type,Values=instance" \ "Name=resource-id,Values=${instanceId}" > ${resultFile} # if API call fails if [ $? -ne 0 ]; then # if attempts exceeded maxRetry if [ "${attempts}" -ge "${maxRetry}" ]; then log "failed to get Instance tags" exit 1 fi else # if API call succeeds break fi attempts=$((attempts + 1)) sleep 10 done } start() { resultFile=$(mktemp --tmpdir=/tmp describe-tags.XXXXXX) instanceId=$(curl -s http://169.254.169.254/latest/meta-data/instance-id) region=$(curl -s http://169.254.169.254/latest/meta-data/placement/availability-zone | sed -e 's/.$//') getInstanceTagWithRetry # Export Variables export APP_ENVIRONMENT=$(cat ${resultFile} | /usr/local/bin/jq -r '.Tags[] | select(.Key == "Environment") | .Value') echo ${APP_ENVIRONMENT} > /etc/environment.d/environment if [ -d /etc/environment.d ]; then for i in /etc/environment.d/*; do if [ -x $i ]; then $i fi done unset i fi } case "$1" in start) start ;; stop) ;; restart) ;; status) ;; *) echo $"Usage: $0 start" exit 2 esac exit $ret
こうすることで、サービス起動時に
- 自分自身のEnvironmentタグを取得
/etc/environment.d/environment
に書き込み/etc/environment.d/
以下のスクリプトファイルを実行
という流れで処理が行われます。配置後、chkconfig environment on
を実行し、OS起動時にこのスクリプトが実行されるようにしておきましょう。
このスクリプトファイルでは、Environmentを取得することのみを実施しています。環境に応じて設定値を変えたい、という話は様々な方向からよく出てくるものなので、個々の設定値切替は/etc/environment.d/
以下のスクリプトで行う方針としています。そうすることで、どのような処理が行われるのかが明確になりやすくなります。また、構成管理ツールとの相性もよくなります。
3のスクリプトの例を出します。例えばApacheの設定ファイルを環境ごとに切り替えたいのであれば以下の用に記載しましょう。
#!/bin/bash set -u ENV=$(cat /etc/environment.d/environment) cp -f /etc/httpd/conf/httpd.conf.${ENV} /etc/httpd/conf/httpd.conf
この場合、本番環境、つまりEnvironmentタグがprdであれば/etc/httpd/conf/httpd.conf.prd
が/etc/httpd/conf/httpd.conf
にコピーされます。
当然、コピー元のファイルは環境ぶん用意しておく必要があります。
また、特定の環境でのみ起動させたいサービスも出てくると思います。ライセンスのあるミドルウェアや監視ツールのエージェントなどですね。そういった場合は以下のように記載します。例としてzabbix-agent
を本番環境でのみ起動したい場合を取り上げると、
#!/bin/bash set -u ENV=$(cat /etc/environment.d/environment) if [ "${ENV}" = "prd" ]; then service zabbix-agent start fi
Environmentタグがprdの時のみ、サービスが起動するようになりました。当然ですが、この形を利用する際にはOS起動時の自動起動設定(chkconfig等)はoffにしておきましょう。
補足
/etc/environment.d/environment
の中でインスタンスのタグ情報を取得しますが、ネットワーク系などが原因で取得に失敗することがあります。AutoScalingで実行している場合は、その時点でインスタンスを停止することで新しいインスタンスに取り替えるのも方法の一つだと思います。AutoScalingではない環境の場合でも、タグの取得に失敗したことを検知できるような仕組みがあると好ましいと思います。 *1
まとめ
Cloud DIと書くと大仰に聞こえますが、実践するメソッドは非常に単純です。Cloud DIのように環境に応じてファイルを適宜切り替える方式には構成管理を行いやすくするメリットもあります。AWSでシステムを開発/運用する場合には是非取り入れてみてはいかがでしょうか。
脚注
- SNS経由でアラートメール送る手もありそうですが、EC2タグが取得できないような状況ではSNSにも届くとは思えないし...悩ましい ↩